#ifndef __MOBIUS__
#define __MOBIUS__


#include "cxfloat.h"
#include <vector>
#include "Pincushion.h"


class Polynomicon;


class Mobius
{
  public:
    Mobius(Polynomicon* pol, std::vector<SeamStress::Seamstress*>* ss, unsigned long int nt=1, mpfr_prec_t p=4);
    ~Mobius();
    
    void transform(const cxfloat& a, const cxfloat& b, const cxfloat& c, const cxfloat& d, std::vector<cxfloat>& inputpoly, cxfloat& shift);
    void transform(const refloat& a, const refloat& b, const refloat& c, const refloat& d, refloat_vec& inputpoly, refloat& shift);
    void transformDers(const cxfloat& a, const cxfloat& b, const cxfloat& c, const cxfloat& d, std::vector<cxfloat>& inputpoly, const cxfloat& shift);
    void transformDers(const refloat& a, const refloat& b, const refloat& c, const refloat& d, refloat_vec& inputpoly, const refloat& shift);
    void poleSeries(const cxfloat& l, unsigned long int M, unsigned long int nterms, std::vector<cxfloat>& series);
    void poleSeries(const refloat& l, unsigned long int M, unsigned long int nterms, refloat_vec& series);
    void matrixMul2D(const std::vector<std::vector<cxfloat> >& M1, const std::vector<std::vector<cxfloat> >& M2, std::vector<std::vector<cxfloat> >& M3);
    void matrixInv2D(const std::vector<std::vector<cxfloat> >& M1, std::vector<std::vector<cxfloat> >& M2);
    void map(const cxfloat& z1, const cxfloat& z2, const cxfloat& z3, const cxfloat& w1, const cxfloat& w2, const cxfloat& w3);
    void compose(const cxfloat& a, const cxfloat& b, const cxfloat& c, const cxfloat& d, const cxfloat& A, const cxfloat& B, const cxfloat& C, const cxfloat& D, cxfloat& a2, cxfloat& b2, cxfloat& c2, cxfloat& d2, cxfloat& A2, cxfloat& B2, cxfloat& C2, cxfloat& D2);
    
    void poleSeriesThread1(void* arg);
    void poleSeriesThreadReal1(void* arg);
    
    
    Polynomicon* getPolynomicon();
    
    void A(cxfloat& val);
    void B(cxfloat& val);
    void C(cxfloat& val);
    void D(cxfloat& val);
    void a(cxfloat& val);
    void b(cxfloat& val);
    void c(cxfloat& val);
    void d(cxfloat& val);
    
  protected:
    SeamStress::Pincushion<Mobius> pins;
    
    mp_prec_t prec;
    
    unsigned long int nthreads;
    
    Polynomicon* poly;
    
  private:
    unsigned long int thread_tot;
    unsigned long int thread_var1;
    
    cxfloat g;
    cxfloat h;
    cxfloat tempval1;
    cxfloat tempval2;
    cxfloat tempval3;
    
    refloat reg;
    refloat reh;
    refloat retempval1;
    refloat retempval2;
    refloat retempval3;
    
    //contains one entry per thread
    std::vector<cxfloat> threadvec1;
    refloat_vec rethreadvec1;
    
    std::vector<cxfloat> tempvec1, tempvec2;
    refloat_vec retempvec1, retempvec2;
    
    std::vector<cxfloat>* tempvecpointer1;
    refloat_vec* retempvecpointer1;
    
  std::vector<std::vector<cxfloat> > h1;
  std::vector<std::vector<cxfloat> > h2;
  std::vector<std::vector<cxfloat> > htemp;
  std::vector<std::vector<cxfloat> > H;
};

inline Polynomicon* Mobius::getPolynomicon(){return poly;}
inline void Mobius::A(cxfloat& val){val.set(h2[0][0]);}
inline void Mobius::B(cxfloat& val){val.set(h2[0][1]);}
inline void Mobius::C(cxfloat& val){val.set(h2[1][0]);}
inline void Mobius::D(cxfloat& val){val.set(h2[1][1]);}
inline void Mobius::a(cxfloat& val){val.set(H[0][0]);}
inline void Mobius::b(cxfloat& val){val.set(H[0][1]);}
inline void Mobius::c(cxfloat& val){val.set(H[1][0]);}
inline void Mobius::d(cxfloat& val){val.set(H[1][1]);}


#endif
